#include "StdAfx.h"
#include "ScriptBind_AI.h"

#include "IAgent.h"
#include "AI\AIManager.h"

#include <IScriptSystem.h>
#include <ScriptHelpers.h>


// TODO(marcio): Exterminate this file


CScriptBind_AI::CScriptBind_AI(ISystem* pSystem)
{
	Init(pSystem->GetIScriptSystem());

	SetGlobalName("EditorAI");
	
	RegisterFunction(
		"AssignPFPropertiesToPathType",
		functor_ret(*this, &CScriptBind_AI::AssignPFPropertiesToPathType));
		
	RegisterFunction(
		"LoadPathfindProperties",
		functor_ret(*this, &CScriptBind_AI::LoadPathfindProperties));

	m_pSS->ExecuteFile("scripts/ai/pathfindProperties.lua", true, true );
}

int CScriptBind_AI::AssignPFPropertiesToPathType(IFunctionHandler *pH)
{
	SCRIPT_CHECK_PARAMETERS(19);

	const char* sPathType = "AIPATH_DEFAULT";
	
	if (pH->GetParamType(1) == svtNumber)
	{
		int nPathType = AIPATH_DEFAULT;
		pH->GetParam(1, nPathType);
		sPathType = GetPathTypeName(static_cast<EAIPathType>(nPathType));
	}
	else
	{
		pH->GetParam(1, sPathType);
	}

#define GET_PF_PARAM(n, s) \
	if ((pH->GetParamCount() >= n) && (pH->GetParamType(n) != svtNull)) \
		pH->GetParam(n, s)

	AgentPathfindingProperties properties;
	
	int navCapMask;
	GET_PF_PARAM( 2, navCapMask);
	properties.navCapMask = navCapMask;

	GET_PF_PARAM( 3, properties.triangularResistanceFactor);
	GET_PF_PARAM( 4, properties.waypointResistanceFactor);
	GET_PF_PARAM( 5, properties.flightResistanceFactor);
	GET_PF_PARAM( 6, properties.volumeResistanceFactor);
	GET_PF_PARAM( 7, properties.roadResistanceFactor);
	GET_PF_PARAM( 8, properties.waterResistanceFactor);
	GET_PF_PARAM( 9, properties.maxWaterDepth);
	GET_PF_PARAM(10, properties.minWaterDepth);
	GET_PF_PARAM(11, properties.exposureFactor);
	GET_PF_PARAM(12, properties.dangerCost);
	GET_PF_PARAM(13, properties.zScale);
	GET_PF_PARAM(14, properties.customNavCapsMask);
	GET_PF_PARAM(15, properties.radius);
	GET_PF_PARAM(16, properties.height);
	GET_PF_PARAM(17, properties.maxSlope);
	GET_PF_PARAM(18, properties.id);
	GET_PF_PARAM(19, properties.avoidObstacles);
	properties.navCapMask |= properties.id * (1 << 24);
#undef GET_PF_PARAM

	// IMPORTANT: If changes are made here be sure to update CScriptBind_AI::AssignPFPropertiesToPathType() in CryAISystem!

	AssignPFPropertiesToPathType(sPathType, properties);

	return pH->EndFunction();
}

//
//-----------------------------------------------------------------------------------------------------------
void CScriptBind_AI::AssignPFPropertiesToPathType(const string& sPathType, AgentPathfindingProperties& properties)
{
	GetIEditor()->GetAI()->AssignPFPropertiesToPathType(sPathType, properties);
}

const char* CScriptBind_AI::GetPathTypeName(EAIPathType pathType)
{
	switch (pathType)
	{
	default:
	case AIPATH_DEFAULT:     return "AIPATH_DEFAULT";

	case AIPATH_HUMAN:       return "AIPATH_HUMAN";
	case AIPATH_HUMAN_COVER: return "AIPATH_HUMAN_COVER";
	case AIPATH_CAR:         return "AIPATH_CAR";
	case AIPATH_TANK:        return "AIPATH_TANK";
	case AIPATH_BOAT:        return "AIPATH_BOAT";
	case AIPATH_HELI:        return "AIPATH_HELI";
	case AIPATH_3D:          return "AIPATH_3D";
	case AIPATH_SCOUT:       return "AIPATH_SCOUT";
	case AIPATH_TROOPER:     return "AIPATH_TROOPER";
	case AIPATH_HUNTER:      return "AIPATH_HUNTER";
	}
	assert(!!!"Should not be here");
	return 0;
}

int CScriptBind_AI::LoadPathfindProperties(IFunctionHandler* pH)
{
	SCRIPT_CHECK_PARAMETERS(1);

	if (pH->GetParamType(1) != svtString)
	{
		gEnv->pLog->LogError("CScriptBind_AI::LoadPathfindProperties expects 1 parameter of type 'string'");
		return pH->EndFunction();
	}

	const char* szFileName = NULL;
	pH->GetParam(1, szFileName);

	const XmlNodeRef root = GetISystem()->LoadXmlFromFile(szFileName);
	if (!root)
	{
		gEnv->pLog->LogError("Unable to load pathfind properties XML file: %s", szFileName);
		return pH->EndFunction();
	}

	if (stricmp(root->getTag(), "Types"))
	{
		gEnv->pLog->LogError("Top-level tag should be <Types> (rather than <%s>) in pathfind properties XML file: %s", root->getTag(), szFileName);
		return pH->EndFunction();
	}

	AgentPathfindingProperties properties;

	typedef std::map<const char*, IAISystem::tNavCapMask, stl::less_stricmp<string> > MapNavCaps;

	MapNavCaps mapNavCaps;
	mapNavCaps["Unset"]             = IAISystem::NAV_UNSET;
	mapNavCaps["Triangular"]        = IAISystem::NAV_TRIANGULAR;
	mapNavCaps["WaypointHuman"]     = IAISystem::NAV_WAYPOINT_HUMAN;
	mapNavCaps["Waypoint3DSurface"] = IAISystem::NAV_WAYPOINT_3DSURFACE;
	mapNavCaps["Flight"]            = IAISystem::NAV_FLIGHT;
	mapNavCaps["Volume"]            = IAISystem::NAV_VOLUME;
	mapNavCaps["Road"]              = IAISystem::NAV_ROAD;
	mapNavCaps["SmartObject"]       = IAISystem::NAV_SMARTOBJECT;
	mapNavCaps["Free2D"]            = IAISystem::NAV_FREE_2D;
	mapNavCaps["Custom"]            = IAISystem::NAV_CUSTOM_NAVIGATION;
	mapNavCaps["Surface"]           = IAISystem::NAVMASK_SURFACE;
	mapNavCaps["Air"]               = IAISystem::NAVMASK_AIR;
	mapNavCaps["All"]               = IAISystem::NAVMASK_ALL;

	for (int iType = 0, nTypes = root->getChildCount(); iType < nTypes; ++iType)
	{
		const XmlNodeRef typeNode = root->getChild(iType);
		if (typeNode->isTag("Type"))
		{
			const char* szPathTypeName = typeNode->getAttr("name");
			if (!szPathTypeName || !(*szPathTypeName))
			{
				gEnv->pLog->LogError("Tag <Type> has no attribute 'name' in pathfind properties XML file: %s, skipping.", szFileName);
				continue;
			}

			properties.SetToDefaults();

			if (const char* szNavCapMask = typeNode->getAttr("navCapMask"))
			{
				properties.navCapMask = 0;

				stack_string sNavCapMask = szNavCapMask;
				int tokenIndex = 0;
				stack_string sNavCap;
				while (!(sNavCap = sNavCapMask.Tokenize(" ,\t", tokenIndex)).empty())
				{
					IAISystem::tNavCapMask navCap = mapNavCaps[sNavCap.c_str()];
					if (navCap)
					{
						properties.navCapMask |= navCap;
					}
					else
					{
						stack_string sWarningMessage = "Unrecognized navigation capability found in pathfind properties XML file: ";
						sWarningMessage += szFileName;
						sWarningMessage += ": ";
						sWarningMessage += sNavCap;
						sWarningMessage += ", skipping.\nValid navigation capabilities (can be comma-separated) are:\n";

						for (MapNavCaps::iterator it = mapNavCaps.begin(), itEnd = mapNavCaps.end(); it != itEnd; ++it)
						{
							if (it->second)
							{
								sWarningMessage += it->first;
								sWarningMessage += "\n";
							}
						}

						gEnv->pLog->LogError(sWarningMessage.c_str());
					}
				}
			}

			typeNode->getAttr("triangularResistanceFactor", properties.triangularResistanceFactor);
			typeNode->getAttr("waypointResistanceFactor",   properties.waypointResistanceFactor);
			typeNode->getAttr("flightResistanceFactor",     properties.flightResistanceFactor);
			typeNode->getAttr("volumeResistanceFactor",     properties.volumeResistanceFactor);
			typeNode->getAttr("roadResistanceFactor",       properties.roadResistanceFactor);
			typeNode->getAttr("waterResistanceFactor",      properties.waterResistanceFactor);
			typeNode->getAttr("maxWaterDepth",              properties.maxWaterDepth);
			typeNode->getAttr("minWaterDepth",              properties.minWaterDepth);
			typeNode->getAttr("exposureFactor",             properties.exposureFactor);
			typeNode->getAttr("dangerCost",                 properties.dangerCost);
			typeNode->getAttr("zScale",                     properties.zScale);
			typeNode->getAttr("customNavCapsMask",          properties.customNavCapsMask);
			typeNode->getAttr("radius",                     properties.radius);
			typeNode->getAttr("height",                     properties.height);
			typeNode->getAttr("maxSlope",                   properties.maxSlope);
			typeNode->getAttr("id",                         properties.id);
			typeNode->getAttr("avoidObstacles",             properties.avoidObstacles);

			AssignPFPropertiesToPathType(szPathTypeName, properties);
		}
		else
		{
			gEnv->pLog->LogError("Found tag <%s> in pathfind properties XML file: %s, skipping.", typeNode->getTag(), szFileName);
		}
	}

	return pH->EndFunction();
}
